﻿using System;
using System.Linq.Expressions;
using System.Web.Mvc;
using System.Web.Routing;
using MvcContrib.TestHelper;
using Assert=Xunit.Assert;

namespace Coderoom.ScrumPlanner.Tests.Unit.Internal
{
	internal static class RouteTestHelper
	{
		/// <summary>
		/// Injects data key-value pairs into the route. This is intended for testing routes with parameters.
		/// </summary>
		/// <param name="routeData">The route data.</param>
		/// <param name="data"></param>
		/// <returns></returns>
		internal static RouteData WithData(this RouteData routeData, object data)
		{
			var dataItems = data.GetType().GetProperties();

			foreach (var dataItem in dataItems)
				routeData.Values.Add(dataItem.Name, dataItem.GetValue(data, null));

			return routeData;
		}

		/// <summary>
		/// Checks whether the the specified route maps to a specific controller.
		/// </summary>
		/// <typeparam name="TController">The expected type of the controller.</typeparam>
		/// <param name="routeData">The route data.</param>
		/// <param name="action">The expected action.</param>
		/// <remarks>
		///	Based on: http://flux88.com/blog/fluent-route-testing-in-asp-net-mvc/.
		/// Refactored to use xUnit rather than nUnit.
		/// </remarks>
		internal static RouteData MapsTo<TController>(this RouteData routeData, Expression<Func<TController, ActionResult>> action) where TController : Controller
		{
			Assert.True(routeData != null, "The URL did not match any route");

			//check controller
			MapsTo<TController>(routeData);

			//check action
			var methodCall = (MethodCallExpression)action.Body;
			// ReSharper disable PossibleNullReferenceException
			var actualAction = routeData.Values.GetValue("action").ToString();
			// ReSharper restore PossibleNullReferenceException
			var expectedAction = methodCall.Method.Name;
			Assert.Equal(actualAction, expectedAction);

			//check parameters
			for (var i = 0; i < methodCall.Arguments.Count; i++)
			{
				var name = methodCall.Method.GetParameters()[i].Name;
				var value = ((ConstantExpression)methodCall.Arguments[i]).Value;
				Assert.Equal(routeData.Values.GetValue(name), value.ToString());
			}

			return routeData;
		}

		/// <summary>
		/// Verifies the <see cref="RouteData">routeData</see> maps to the controller type specified.
		/// </summary>
		/// <typeparam name="TController"></typeparam>
		/// <param name="routeData"></param>
		/// <returns></returns>
		internal static RouteData MapsTo<TController>(this RouteData routeData) where TController : Controller
		{
			//strip out the word 'Controller' from the type
			var expected = typeof (TController).Name.Replace("Controller", "");

			//get the key (case insensitive)
			var actual = routeData.Values.GetValue("controller").ToString();

			Assert.Equal(expected, actual);
			return routeData;
		}
	}
}